﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Krilib.Bridges;
using Krilib.Data;
using Krile.Data;
using System.ComponentModel;

namespace Krile.Cores
{
    public class StatusManager : IStatusManager
    {
        public event Common.StatusRegistedCallback OnStatusRegistered;

        public event Common.StatusRegistedCallback OnStatusRemoved;

        internal delegate void XStatusRegisteredCallback(IEnumerable<XStatus> xstatuses);

        internal event XStatusRegisteredCallback OnXStatusRegistered;

        internal event XStatusRegisteredCallback OnXStatusRemoved;

        /// <summary>
        /// Main storage of status array
        /// </summary>
        private Dictionary<ulong, XStatus> statusStorage;

        object psdLocker = new object();
        /// <summary>
        /// Preset search data list
        /// </summary>
        private Dictionary<string, Type> presetSearchDataList;

        private static object uidLocker = new object();
        private ulong uidSource = 1;

        internal StatusManager()
        {
            statusStorage = new Dictionary<ulong, XStatus>();
            presetSearchDataList = new Dictionary<string, Type>();
        }

        internal XStatus[] GetAllStatuses()
        {
            var rets = new List<XStatus>();
            lock (statusStorage)
            {
                foreach (var val in statusStorage.Values)
                    rets.Add(val);
            }
            return rets.ToArray();
        }

        internal SearchData[] GetAllSearchData()
        {
            List<SearchData> ret = new List<SearchData>();

            lock (psdLocker)
            {
                foreach (var val in presetSearchDataList.Values)
                    ret.Add((SearchData)Activator.CreateInstance(val));
            }
            return ret.ToArray();
        }

        public Status[] ExtractStatuses(SearchDataBase sdb)
        {
            var array = ExtractXStatuses(sdb);
            List<Status> ova = new List<Status>();
            foreach (var ai in array)
                ova.Add(ai.BaseStatus);
            return ova.ToArray();
        }

        internal XStatus[] ExtractXStatuses(SearchDataBase sdb)
        {
            List<XStatus> matched = new List<XStatus>();
            lock (statusStorage)
            {
                foreach (var value in statusStorage.Values)
                {
                    if (sdb == null || sdb.IsMatched(value.BaseStatus))
                        matched.Add(value);
                }
            }
            return matched.ToArray();
        }

        public ulong GetNextUID()
        {
            lock (uidLocker)
                return uidSource++;
        }

        public Status GetStatus(ulong uid)
        {
            lock (statusStorage)
            {
                if (statusStorage.ContainsKey(uid))
                    return statusStorage[uid].BaseStatus;
                else
                    return null;
            }
        }

        public bool IdExists(ulong uid)
        {
            return statusStorage.ContainsKey(uid);
        }

        public SearchData LookupSearchData(string sdid)
        {
            if (presetSearchDataList.ContainsKey(sdid))
                return (SearchData)Activator.CreateInstance(presetSearchDataList[sdid]);
            else
                return null;
        }

        public void RegistNewSearchData(Type searchDataType)
        {
            if (!searchDataType.IsSubclassOf(typeof(SearchData)))
                throw new KrilePluginExecutionException("Argument isn't inherit \"Krilib.Data.SearchData\".");
            SearchData sd = (SearchData)Activator.CreateInstance(searchDataType);
            if (sd.Identification == null)
                throw new KrilePluginExecutionException("Argument's id is not valid.You can't use null for id.");
            if (presetSearchDataList.ContainsKey(sd.Identification))
                throw new KrilePluginExecutionException("Argument's id:\"" + sd.Identification + "\" is already exists in register.");
            presetSearchDataList.Add(sd.Identification, searchDataType);
        }

        public void RegistStatus(IEnumerable<Status> statuses)
        {
            RegistStatus(statuses, true);
        }

        public void RegistStatus(IEnumerable<Status> statuses, bool isNew)
        {
            List<XStatus> regXStatus = new List<XStatus>();
            foreach (var status in statuses)
            {
                if (Core.Config.Timeline.NGSDClusters != null &&
                    Core.Config.Timeline.NGSDClusters.IsMatched(status))
                    continue;
                if (IdExists(status.UID))
                    throw new KrileManagementException("Status which has id:" + status.UID.ToString() + " is already exists in status storage.");
                if (status.ParentAgent.Status.AutoCreateFavOperationTriggers || status.ParentAgent.Status.AutoCreateReplyOperationTriggers)
                {
                    List<Krilib.Control.Surface.CommandButton> cmdbtns = new List<Krilib.Control.Surface.CommandButton>();
                    if (status.commands != null)
                        cmdbtns.AddRange(status.commands);
                    if (status.ParentAgent.Status.AutoCreateFavOperationTriggers)
                        cmdbtns.Add(Data.DefaultCommandButtons.GetFavButton(status));
                    if(status.ParentAgent.Status.AutoCreateReplyOperationTriggers)
                        cmdbtns.Add(Data.DefaultCommandButtons.GetReplyButton(status));
                    status.commands = cmdbtns.ToArray();
                }
                XStatus n = new XStatus(status);
                n.Unread = isNew;
                statusStorage.Add(status.UID, n);
                regXStatus.Add(n);
            }

            Core.NotificationManager.BeginSleep();
            if (OnStatusRegistered != null)
                OnStatusRegistered.Invoke(statuses);
            if (OnXStatusRegistered != null)
                OnXStatusRegistered.Invoke(regXStatus.ToArray());
            Core.NotificationManager.EndSleep();
        }

        public void RemoveStatus(ulong[] rmuids)
        {
            List<XStatus> delXStatus = new List<XStatus>();
            List<Status> delStatus = new List<Status>();
            foreach (var uid in rmuids)
            {
                if (!IdExists(uid))
                    throw new KrileManagementException("Status which has id:" + uid.ToString() + " isn't exists in status storage.");
                XStatus xs = statusStorage[uid];
                delStatus.Add(xs.BaseStatus);
                delXStatus.Add(xs);
                statusStorage.Remove(uid);
            }

            if (OnStatusRemoved != null)
                OnStatusRemoved.Invoke(delStatus.ToArray());
            if (OnXStatusRemoved != null)
                OnXStatusRemoved.Invoke(delXStatus.ToArray());
        }

        internal static class StatusServerSorts
        {
            internal static int StatusSortComparison(XStatus x, XStatus y)
            {
                int vector = x.BaseStatus.postedDate.CompareTo(y.BaseStatus.postedDate);
                if (vector == 0)
                    vector = (int)x.BaseStatus.UID - (int)y.BaseStatus.UID;

                return Core.Config.Timeline.ListBottomNewer ? vector : -vector;
            }

            public static event Action StatusSortComparisonUpdated;
            public static void StatusSortComparisonUpdateInvoke()
            {
                if (StatusSortComparisonUpdated != null)
                    StatusSortComparisonUpdated.Invoke();
            }
        }

    }
}
